void init_muart()
{ 
  uartbuffer_first = 0; uartbuffer_last = 0; uartbuffer_size = 0;
  uart_buffer = NULL;
 
  if (TOS_NODE_ID == BASESTATION_ADDR)
  {
    uart_buffer = (UARTBUFFER_ENTRY *) mcalloc(UARTBUFFER_SIZE, sizeof(UARTBUFFER_ENTRY));
    memset(uart_buffer, 0, sizeof(uart_buffer));   
  }
}

uint8_t uart_hassingletreemsgs()
{            // checks if uart buffer has outgoing messages which need only single tree to be distributed
  int i;     // hold on to queries until all trees are finished
  uint8_t cmd;

  for (i = 0; i < UARTBUFFER_SIZE; i++)
    if (uart_buffer[i].incoming == 0 && uart_buffer[i].data_len > 0)
    {
      cmd = (uart_buffer[i].data[0] & CMDHI_MASK);
      if (cmd == CMDHI_SUMMARYSPEC || cmd == CMDHI_ATTRUPDATE || cmd == CMDHI_STATS)
        return 1;
    }

  return 0;
}

uint8_t process_uartmsg(uint8_t *data_len, uint8_t **data, uint8_t *pred_count, ROUTING_PREDICATE **preds)   
{                                      // called from UART, 0: dont process, 1: process with base flood predicates
  STATS_MSGHI msg_in1;
  QUERYSPECP_MSGHI msg_in;
  QUERY_ENTRY *p;
  uint32_t newbegin_time, newend_time, t;
  int i;

  if (tree_busy)    // keep msgs in queue while parents on 1st tree are selected
    return 0;

  if (peer_discovery && timer_cycles < phase_start+DISCOVERY_EPOCHS)
    return 0;       // keep in queue if need to do more peer discovery

  switch ((*data)[0] & CMDHI_MASK)
  {
    case CMDHI_SUMMARYSPEC:
      if (peer_discovery)
      {
        start_treeconstruct();                        // if doing discovery & waiting, start tree construct
        return 0;
      } else if (trees_built)                // if trees already built, start new consturction
      {
        start_treeconstruct();
        reset_treesummaries();
        return 0;
      } else return 1;

    case CMDHI_ATTRUPDATE:
       return !peer_discovery;
       
    case CMDHI_QUERY:
      if (trees_built)
      {
        msg_in.spec = NULL;
        unmarshal_queryspecpmsghi(&msg_in, *data);

        p = &query_buffer[msg_in.query_id];
        if (msg_in.running && p->used == 0)
        {
          msg_in.spec = &p->spec;
          unmarshal_queryspecpmsghi(&msg_in, *data);
          
          t = timer_cycles + QUERYDISTRIB_FACTOR*trees[0].deepest_hops*DIV_CEIL(*data_len, MAX_GENERICPAYLOAD) + 
            ((msg_in.spec->srcrel_count > 1) ? QUERYINIT_FACTOR*trees[0].deepest_hops : 0);
          newbegin_time = seconds_to_timercycles(p->spec.params[PARAM_BEGIN]) + t;
          newend_time = seconds_to_timercycles(p->spec.params[PARAM_END]) + t;  //convert seconds to cycles
          
          DBGOUT("Query %d begin updated from %d to %d, end updated from %d to %d", msg_in.query_id,
            p->spec.params[PARAM_BEGIN], newbegin_time, p->spec.params[PARAM_END], newend_time);
          p->spec.params[PARAM_BEGIN] = newbegin_time;
          p->spec.params[PARAM_END] = newend_time;
          p->spec.params[PARAM_WINDOWADVANCETIME] = seconds_to_timercycles(p->spec.params[PARAM_WINDOWADVANCETIME]);
          p->spec.params[PARAM_SAMPLEINTERVAL] = seconds_to_timercycles(p->spec.params[PARAM_SAMPLEINTERVAL]);

          *data = (uint8_t *) mrealloc(*data, marshalest_queryspecpmsghi(&msg_in));
          *data_len = marshal_queryspecpmsghi(&msg_in, *data);
        };

        return 1;
      } else return 0;
    
    case CMDHI_STATS: 
      unmarshal_statsmsghi(&msg_in1, *data);
      
      if (peer_discovery)
      {
        if (msg_in1.type == STATTYPE_RESET)   
          start_treeconstruct();           // initiate tree construction with default summaries so that we have a tree to deliver the stat request
        return 0;
      } else {                     // if stats required from a specific node, use ID summary for efficient routing if we have one
        if (msg_in1.type != STATTYPE_RESPONSE && msg_in1.dest_node != AM_BROADCAST_ADDR)        
          for (i = 0; i < summarydesc_count; i++)
            if (summary_desc[i].attrdesc_index == ATTR_ID)
            {
              *pred_count = 1;
              *preds = (ROUTING_PREDICATE *) mmalloc(sizeof(ROUTING_PREDICATE));
              (*preds)[0].summary_index = i;
              (*preds)[0].value = msg_in1.dest_node;
              (*preds)[0].value_ext = 0;          
              break;
            }  
        
        return 1;
      }  
      
    default: return trees_built;     // other messages: process when we have all trees constructed
  }
}

uint8_t update_uartbuffer(uint8_t *data, uint8_t *data_len)
{
  int i;
  UARTBUFFER_ENTRY *p;
  uint8_t pred_count;
  ROUTING_PREDICATE *preds;

  if (TOS_NODE_ID != BASESTATION_ADDR)
  {
    DBGERR("Only the base station has UART buffer");
    pause_sim();
    return 1;
  }

  *data_len = 0;
  for (i = 0; i < UARTBUFFER_THROTTLE; i++)
  {
    if (uartbuffer_size == 0)
      break;

    p = &uart_buffer[uartbuffer_first];
    if (p->data_len > 0)
    {
      if (p->incoming)
      {
        DBGOUT("Processing incoming uart message at position %d", uartbuffer_first);
        *data_len = p->data_len;
        memcpy(data, p->data, p->data_len);
      } else {  
        pred_count = 0;
        preds = NULL;
        if (process_uartmsg(&p->data_len, &p->data, &pred_count, &preds) == 0)
          break;
        
        DBGOUT("Processing outgoing uart message at position %d", uartbuffer_first);   
        init_baseflood(pred_count, preds, p->data_len, p->data);
        
        if (preds)
          mfree(preds);
      }
    }  

    mfree(p->data);
    p->data_len = 0;
    uartbuffer_first = (uartbuffer_first+1) % UARTBUFFER_SIZE;
    uartbuffer_size--;

    if (*data_len > 0)
      return 0;
  }

  return 1;
}

void enqueue_uartmsg(uint8_t incoming, uint8_t data_len, uint8_t *data, ADDRESS source_node)
{
  UARTBUFFER_ENTRY *p;
  uint8_t *p1;
  int i;
  STATS_MSGHI msg;

  if (TOS_NODE_ID != BASESTATION_ADDR)
  {
    DBGERR("Only the base station has UART buffer");
    pause_sim();
    return;
  }

  if (data_len > 0 && (data[0] & CMDHI_MASK) == CMDHI_STATS)
  {
    unmarshal_statsmsghi(&msg, data);      // clear uart buffer on reset command
    if (msg.type == STATTYPE_RESET)
    {
      DBGOUT("Clearing uart buffer");
      for (i = 0; i < UARTBUFFER_SIZE; i++)
      {
        if (uart_buffer[i].data)
        {
          mfree(uart_buffer[i].data);
          uart_buffer[i].data = NULL;
        }  
        uart_buffer[i].data_len = 0;
        uart_buffer[i].incoming = 0;
      }
    
      uartbuffer_first = 0; uartbuffer_last = 0; uartbuffer_size = 0;
    }
  }

  if (uartbuffer_size >= UARTBUFFER_SIZE)
  {
#ifdef TOSSIM
    DBGERR("Cannot enqueue message in uart buffer!");
    log_dropcmd(1, &TOS_NODE_ID, data, data_len, 7);
    pause_sim();
#endif    
    return;
  }

  p = &uart_buffer[uartbuffer_last];
  p->incoming = incoming;
  p->data_len = data_len + (incoming ? sizeof(ADDRESS) : 0);
  if (data_len > 0)
  {
    p->data = mmalloc(p->data_len);
    memcpy(p->data, data, data_len);
    if (incoming)
    {
      p1 = &p->data[data_len];
      marshal_word(&p1, source_node);
    }
  } else {
    p->data_len = 0;
    p->data = NULL;
  }

  DBGOUT("Enqueued %s uart message at position %d", incoming ? "incoming" : "outgoing", uartbuffer_last);
  uartbuffer_last = (uartbuffer_last+1) % UARTBUFFER_SIZE;
  uartbuffer_size++;
}
